//
// Created by 顾蛟 on 2020/7/4.
//

#include "GLUtils.h"

GLuint GLUtils::LoadShader(GLenum type, const char *shaderSource) {
    GLuint shader = 0;
    GLint compiled = 0;
    shader = glCreateShader(type);
    if (shader == 0) {
        return 0;
    }
    //count是指出shadersource里面包含几个shader。
    //length为NULL时表示string以null结束，不为null时，指向一个int数组，这个数组里面存储的是每个shader字符串的长度。
    glShaderSource(shader, 1, &shaderSource, NULL);
    glCompileShader(shader);
    //GL_SHADER_TYPE, GL_DELETE_STATUS, GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH, GL_SHADER_SOURCE_LENGTH.
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    if (!compiled) {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 0) {
            char *info = (char*) malloc(infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, info);
            LOGCATE("GLUtils::LoadShader Could not compile shader %d:\n%s\n", type, info);
            free(info);
        }
        glDeleteShader(shader);
        shader = 0;
    }
    return shader;
}

GLuint GLUtils::CreateProgram(const char *vertexShaderSource, const char *fragShaderSource,
        GLuint &vertexHandler, GLuint &fragShaderHandler) {
    GLuint program = 0;
    GLint linkStatus = GL_FALSE;
    vertexHandler = LoadShader(GL_VERTEX_SHADER, vertexShaderSource);
    if (!vertexHandler)
        return program;
    fragShaderHandler = LoadShader(GL_FRAGMENT_SHADER, fragShaderSource);
    if (!fragShaderHandler)
        return program;

    program = glCreateProgram();
    if (program == 0) {
        return program;
    }

    glAttachShader(program, vertexHandler);
    CheckGLError("glAttachShader vertex");
    glAttachShader(program, fragShaderHandler);
    CheckGLError("glAttachShader fragment");
    glLinkProgram(program);

    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
    if (linkStatus != GL_TRUE) {
        GLint infoLen = 0;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
        if (infoLen > 0) {
            char *info = (char*) malloc(infoLen);
            glGetProgramInfoLog(program, infoLen, NULL, info);
            free(info);
        }
        glDeleteProgram(program);
        program = 0;
    }
    return program;
}

void GLUtils::DeleteProgram(GLuint &program) {
    if (program) {
        glUseProgram(0);
        glDeleteProgram(program);
        program = 0;
    }
}

void GLUtils::CheckGLError(const char *GLOperation) {
    for (GLint error = glGetError(); error; error = glGetError()) {
        switch (error) {
            case GL_NO_ERROR:
                //到这个switch语句，就一定是有错。
                break;
            case GL_INVALID_ENUM:
                //%x是以十六进制显示
                LOGCATE("GLUtils::CheckGLError GL Operation %s() glError(0x%x), GL_INVALID_ENUM\n", GLOperation, error);
                break;
            case GL_INVALID_VALUE:
                LOGCATE("GLUtils::CheckGLError GL Operation %s() glError(0x%x), GL_INVALID_VALUE\n", GLOperation, error);
                break;
            case GL_INVALID_OPERATION:
                LOGCATE("GLUtils::CheckGLError GL Operation %s() glError(0x%x), GL_INVALID_OPERATION\n", GLOperation, error);
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                LOGCATE("GLUtils::CheckGLError GL Operation %s() glError(0x%x), GL_INVALID_FRAMEBUFFER_OPERATION\n", GLOperation, error);
                break;
            case GL_OUT_OF_MEMORY:
                LOGCATE("GLUtils::CheckGLError GL Operation %s() glError(0x%x), GL_OUT_OF_MEMORY\n", GLOperation, error);
                break;
            default:
                break;
        }
    }
}
